<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ElDialog, ElButton, ElMessage } from 'element-plus'
import { FinanceService } from '/@/api/openapi'

// 声明 lamejs 变量
declare const lamejs: any

// 声明 AudioContext 类型
declare global {
  interface Window {
    webkitAudioContext: typeof AudioContext
  }
}

// 定义一个类型来约束转换后的数据
type RecordItem = {
  name: string
  type: string
  category: string
  amount: number
  remark?: string
}

const props = defineProps<{
  date: string
  visible: boolean
}>()

const emit = defineEmits<{
  (e: 'update:visible', value: boolean): void
  (e: 'add-success', record: RecordItem & { date: string }): void
}>()

// 状态变量
const isRecording = ref(false)
const isLoading = ref(false)
const isLamejsLoaded = ref(false)
const recordedData = ref<Blob | null>(null)
const mediaRecorder = ref<MediaRecorder | null>(null)
const voiceRecognitionResult = ref<RecordItem[]>([])

// 加载 lamejs 脚本
const loadLamejs = () => {
  return new Promise((resolve, reject) => {
    if ((window as any).lamejs) {
      isLamejsLoaded.value = true
      resolve(true)
      return
    }
    const script = document.createElement('script')
    script.src = 'https://cdn.jsdelivr.net/npm/lamejs@1.2.1/lame.min.js'
    script.onload = () => {
      console.log('lamejs 加载成功')
      isLamejsLoaded.value = true
      resolve(true)
    }
    script.onerror = error => {
      console.error('lamejs 加载失败:', error)
      isLamejsLoaded.value = false
      reject(error)
    }
    document.head.appendChild(script)
  })
}

// 在组件挂载时加载 lamejs
onMounted(async () => {
  isLoading.value = true
  try {
    await loadLamejs()
  } catch (error) {
    console.error('lamejs 加载失败:', error)
    ElMessage.error('音频转换组件加载失败')
  } finally {
    isLoading.value = false
  }
})

// 将音频数据转换为 PCM
const convertToPCM = async (audioBlob: Blob): Promise<Float32Array> => {
  const audioContext = new (window.AudioContext || window.webkitAudioContext)()
  const arrayBuffer = await audioBlob.arrayBuffer()
  const audioBuffer = await audioContext.decodeAudioData(arrayBuffer)

  // 获取单声道数据
  const channelData = audioBuffer.getChannelData(0)
  return channelData
}

// 将 Float32Array 转换为 Int16Array
const convertFloat32ToInt16 = (float32Array: Float32Array): Int16Array => {
  const int16Array = new Int16Array(float32Array.length)
  for (let i = 0; i < float32Array.length; i++) {
    // 将 float32 (-1,1) 转换为 int16 (-32768,32767)
    const s = Math.max(-1, Math.min(1, float32Array[i]))
    int16Array[i] = s < 0 ? s * 0x8000 : s * 0x7fff
  }
  return int16Array
}

// 将音频数据转换为 MP3
const convertToMP3 = async (audioBlob: Blob): Promise<Blob> => {
  try {
    console.log('开始音频转换流程...')
    console.log('1. 将音频解码为 PCM 数据')
    const floatPCM = await convertToPCM(audioBlob)

    console.log('2. 将 Float32Array 转换为 Int16Array')
    const pcmData = convertFloat32ToInt16(floatPCM)

    console.log('3. 开始 MP3 编码')
    const encoder = new lamejs.Mp3Encoder(1, 44100, 128)
    const maxSamples = 1152
    const mp3Data = []

    // 分块处理音频数据
    for (let i = 0; i < pcmData.length; i += maxSamples) {
      const pcm = pcmData.subarray(i, Math.min(i + maxSamples, pcmData.length))
      const encoded = encoder.encodeBuffer(pcm)
      if (encoded.length > 0) {
        mp3Data.push(encoded)
      }
    }

    // 获取剩余的 MP3 数据
    const lastData = encoder.flush()
    if (lastData.length > 0) {
      mp3Data.push(lastData)
    }

    // 合并所有 MP3 数据
    const blob = new Blob(mp3Data, { type: 'audio/mp3' })
    console.log('MP3 转换完成，大小:', blob.size, '字节')
    return blob
  } catch (error) {
    console.error('MP3 转换失败:', error)
    throw error
  }
}

// 开始录音
const startRecording = async () => {
  if (!isLamejsLoaded.value) {
    ElMessage.error('音频转换组件未加载，请刷新页面重试')
    return
  }

  try {
    const stream = await navigator.mediaDevices.getUserMedia({
      audio: {
        channelCount: 1, // 单声道
        sampleRate: 44100, // 采样率
        echoCancellation: true, // 回声消除
        noiseSuppression: true, // 噪声抑制
      } as MediaTrackConstraints,
    })

    // 使用 webm 格式录制
    mediaRecorder.value = new MediaRecorder(stream, {
      mimeType: 'audio/webm;codecs=opus',
      audioBitsPerSecond: 128000, // 设置比特率为 128kbps
    })

    const chunks: BlobPart[] = []

    mediaRecorder.value.ondataavailable = e => {
      if (e.data.size > 0) {
        chunks.push(e.data)
      }
    }

    // 设置每秒触发一次 ondataavailable 事件
    mediaRecorder.value.start(1000)
    isRecording.value = true

    mediaRecorder.value.onstop = async () => {
      isLoading.value = true
      try {
        console.log('=== 录音完成 ===')
        // 合并录音数据
        const audioBlob = new Blob(chunks, { type: 'audio/webm;codecs=opus' })
        console.log('录音数据大小:', audioBlob.size, '字节')

        // 转换为 MP3
        console.log('开始转换为 MP3...')
        const mp3Data = await convertToMP3(audioBlob)
        console.log('MP3数据大小:', mp3Data.size, '字节')

        // 准备上传数据
        const requestData = {
          file: mp3Data,
        }

        console.log('开始识别...')
        const response = await FinanceService.automatic(
          'multipart/form-data',
          requestData,
        )

        if (response.code === 200 && response.data) {
          console.log('原始数据:', response.data)
          // 转换数据格式并过滤掉无效数据
          const convertedData: RecordItem[] = (response.data || [])
            .filter(
              item =>
                item && item.name && item.type && item.category && item.amount,
            )
            .map(item => {
              const record: RecordItem = {
                name: String(item.name || ''),
                type: String(item.type === 2 ? '支出' : '收入'),
                category: String(item.category || '其他'),
                amount: Number(item.amount) || 0,
              }
              if (item.remark) {
                record.remark = String(item.remark)
              }
              return record
            })

          if (convertedData.length > 0) {
            voiceRecognitionResult.value = convertedData
            ElMessage.success(`成功识别${convertedData.length}条记录`)
          } else {
            ElMessage.warning('未能识别出有效的记录')
          }
        } else {
          console.log(response.message)
          ElMessage.error(response.message || '识别失败')
        }
      } catch (error) {
        console.error('处理录音失败:', error)
        ElMessage.error('处理录音失败')
      } finally {
        isLoading.value = false
        // 清理资源
        stream.getTracks().forEach(track => track.stop())
      }
    }
  } catch (err) {
    console.error('无法访问麦克风:', err)
    ElMessage.error('无法访问麦克风')
  }
}

// 停止录音
const stopRecording = () => {
  if (mediaRecorder.value && isRecording.value) {
    mediaRecorder.value.stop()
    isRecording.value = false
    mediaRecorder.value.stream.getTracks().forEach(track => track.stop())
  }
}

// 确认添加记录
const confirmVoiceRecord = (record: RecordItem) => {
  emit('add-success', {
    ...record,
    date: props.date,
  })
  // 从结果列表中移除已确认的记录
  voiceRecognitionResult.value = voiceRecognitionResult.value.filter(
    item => item !== record,
  )
  // 如果所有记录都已确认，关闭对话框
  if (voiceRecognitionResult.value.length === 0) {
    closeDialog()
  }
}

// 关闭对话框
const closeDialog = () => {
  emit('update:visible', false)
  voiceRecognitionResult.value = []
  isRecording.value = false
  if (mediaRecorder.value) {
    mediaRecorder.value.stream.getTracks().forEach(track => track.stop())
  }
}
</script>

<template>
  <el-dialog
    :model-value="visible"
    @update:model-value="emit('update:visible', $event)"
    title="语音录入"
    width="500px"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    @close="closeDialog"
  >
    <div class="voice-dialog-content">
      <div v-if="isLoading" class="loading-overlay">
        <i class="el-icon-loading"></i>
        <p>处理中...</p>
      </div>

      <div class="recording-status">
        <div class="record-icon" :class="{ recording: isRecording }">
          <i class="fa fa-microphone"></i>
        </div>
        <p>{{ isRecording ? '正在录音...' : '点击开始录音' }}</p>
      </div>

      <div v-if="voiceRecognitionResult.length > 0" class="recognition-results">
        <h4>识别结果：</h4>
        <div
          v-for="(record, index) in voiceRecognitionResult"
          :key="index"
          class="result-card"
        >
          <div class="result-header">
            <span class="result-title">记录 {{ index + 1 }}</span>
            <el-button
              type="success"
              size="small"
              @click="confirmVoiceRecord(record)"
            >
              确认添加
            </el-button>
          </div>
          <div class="result-content">
            <div class="result-item">
              <span class="label">名称：</span>
              <span>{{ record.name }}</span>
            </div>
            <div class="result-item">
              <span class="label">类型：</span>
              <span>{{ record.type }}</span>
            </div>
            <div class="result-item">
              <span class="label">分类：</span>
              <span>{{ record.category }}</span>
            </div>
            <div class="result-item">
              <span class="label">金额：</span>
              <span>¥{{ record.amount }}</span>
            </div>
            <div class="result-item">
              <span class="label">备注：</span>
              <span>{{ record.remark || '-' }}</span>
            </div>
          </div>
        </div>
      </div>
    </div>

    <template #footer>
      <div class="dialog-footer">
        <el-button @click="closeDialog">关闭</el-button>
        <el-button
          v-if="!isRecording && voiceRecognitionResult.length === 0"
          type="primary"
          @click="startRecording"
          :disabled="isLoading || !isLamejsLoaded"
        >
          {{ !isLamejsLoaded ? '组件加载中...' : '开始录音' }}
        </el-button>
        <el-button
          v-if="isRecording"
          type="warning"
          @click="stopRecording"
          :disabled="isLoading"
        >
          停止录音
        </el-button>
      </div>
    </template>
  </el-dialog>
</template>

<style scoped>
.voice-dialog-content {
  padding: 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  position: relative;
  min-height: 200px;
}

.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(255, 255, 255, 0.9);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 100;
}

.loading-overlay i {
  font-size: 32px;
  color: #409eff;
  margin-bottom: 10px;
}

.loading-overlay p {
  margin: 0;
  color: #666;
}

.recording-status {
  text-align: center;
}

.record-icon {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: #f5f5f5;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 10px;
  font-size: 24px;
  color: #666;
}

.record-icon.recording {
  background: #f56c6c;
  color: white;
  animation: pulse 1.5s infinite;
}

@keyframes pulse {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.1);
  }
  100% {
    transform: scale(1);
  }
}

.recognition-results {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.recognition-results h4 {
  margin: 0;
  color: #333;
}

.result-card {
  background: #f8f8f8;
  border-radius: 8px;
  padding: 16px;
  border: 1px solid #eee;
}

.result-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid #eee;
}

.result-title {
  font-weight: 500;
  color: #333;
}

.result-content {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.result-item {
  display: flex;
  align-items: center;
}

.result-item .label {
  width: 60px;
  color: #666;
}

.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
</style>